//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#include "Switch.h"

Define_Module(Switch);

void Switch::initialize()
{
    switchMAC = "aa:bb:cc:dd:ee:ff";
    agingTime = par("ageingTime");

    // enum types
    IPv4_type = 800;
    ARP_type  = 806;
    ARP_request  = 807;
    ARP_reply  = 808;

    //filteringDatabase initialization
    for(int i=0; i<NUM_OF_GATES ; i++)
        filteringDatabase[i]="";

}

void Switch::handleMessage(cMessage *msg)
{
    if(dynamic_cast<Ageing *>(msg))
        handleAgeingMessage(check_and_cast<Ageing *>(msg));
    else if(dynamic_cast<Eth_pck *>(msg))
    {
        if(msg->getArrivalGate() != NULL)
        {
            int gateIndex = msg->getArrivalGate()->getIndex();
            handleEthMessage(check_and_cast<Eth_pck *>(msg), gateIndex);
        }
    }
    else
        drop(msg);

}

void Switch::handleEthMessage(Eth_pck *msg, int gateIndex)
{
    if(msg->isSelfMessage())
        forwardMessage(msg, msg->getIntendedGate());
    int srcGate = macEntry(msg->getMAC_source());
    int dstGate = macEntry(msg->getMAC_dest());
    if(srcGate < 0)                                 //if source address not exists
    {
        filteringDatabase[gateIndex] = msg->getMAC_source();
        Ageing *ttl = new Ageing("Time To Live");
        scheduleAt(simTime() + agingTime, ttl);
    }
    if(dstGate < 0)                                 //if destination address not exists
        broadcastMessage(msg, gateIndex);
    else
        forwardMessage(msg, dstGate);
}

void Switch :: handleAgeingMessage(Ageing *msg)
{
    EV<<"Filtering Database "<<msg->getTableIndex()<<" expired"<<endl;
    filteringDatabase[msg->getTableIndex()]="";
}

void Switch::forwardMessage(Eth_pck *msg, int gateIndex)
{
    cChannel *channel = gate("toHost", gateIndex)->getTransmissionChannel();
    simtime_t txFinishTime = channel->getTransmissionFinishTime();
    msg->setIntendedGate(gateIndex);                                //in case it will become self message
    if(txFinishTime <= simTime())
    {
        EV<<msg->getName()<<" sent from swith to host"<<(gateIndex+1)<<endl;
        send(msg,"toHost",gateIndex);
    }
    else
        scheduleAt(txFinishTime, msg);
}

int Switch :: macEntry(string address)
{
    int tableIndex = -1;
    for(int i=0; i<NUM_OF_GATES ; i++)
    {
        string temp(filteringDatabase[i]);
        if(temp.size() > 16 && temp.compare(temp.size()-17,17, address,address.size()-17,17) == 0)
        {
            tableIndex = i;
            break;
        }
    }
    return tableIndex;
}

void Switch::broadcastMessage(Eth_pck *msg, int gateIndex)
{
    for(int i=0 ; i < NUM_OF_GATES ; i++)
    {
        if (i==gateIndex)
            continue;
        cChannel *channel = gate("toHost", i)->getTransmissionChannel();
        double delay = (msg->getByteLength()*0.000003)/1000;
        if (!channel->isBusy())
        {
            sendDelayed(msg->dup(), delay, "toHost", i);
        }
        else
        {
            Eth_pck *duplicated = msg->dup();
            duplicated -> setIntendedGate(i);
            scheduleAt(simTime() + delay, duplicated);
        }
    }
}


